#!/usr/bin/env python
# original code by h3k ( michael vandenberghe ) 
# modified by: Inc (Ian Caldwell)
sharedPath="/usr/share/capsules"
imagePath=sharedPath+"/images/"

import getopt, sys, os
sys.path.append(sharedPath)
import capsules

import pygtk, gtk

db=capsules.capWeb()
db.alias='stable'
db.depalias='stable'

version="2.0"
authors=[
	'michael vandenberghe - h3k@arcanelinux.org'
    'ian Caldwell - inc@inc-corporate.org'
        ]

def finishEvents():
	while gtk.events_pending():
		gtk.main_iteration(False)


class pygtk_installer(capsules.ui):
	"""
		The interface to the frontend, whatever code utilizes this module should follow this interface.
		This class will allow the backend, to communicate progress/data to the frontend.
		The default frontend provided here, is a command line front end.
	"""
	cont=True
	window=None
	
	def appendTextBuffer(self,text):
		self.label.set_text(text)
		bounds=self.txt.get_bounds()
		old = self.txt.get_text(bounds[0],bounds[1])
		sep=os.linesep
		self.txt.set_text(old + sep + '- '+text)
	
	def delete(self, widget,event=None):
		self.cont = False
		self.window.destroy()
		self.window=None
		
	def cancel(self,widget):
		self.appendTextBuffer("Cancelled")
		self.cont=False
	
	def __init__(self):
		self.txt = gtk.TextBuffer()
		self.cont=True
		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		self.image=gtk.Image()
		self.progress=gtk.ProgressBar()
		self.label=gtk.Label('cap-pygtk')
		self.txtV = gtk.TextView(self.txt)
		self.txtV.set_editable(False)
		
		self.hbox=gtk.HBox(False,8)
		self.hbox.pack_start(self.image,False,False,0)
		self.hbox.pack_start(self.label,False,False,0)
		
		self.btn_cancel=gtk.Button(stock=gtk.STOCK_CANCEL)
		
		self.vbox2=gtk.VBox(False,8)
		self.vbox2.pack_start(self.hbox,False,False,0)
		self.vbox2.pack_start(self.progress,False,False,0)
		self.scroller=gtk.ScrolledWindow()
		self.scroller.add_with_viewport(self.txtV)
		self.scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		self.vbox2.pack_start(self.scroller,True,True,0)
		self.vbox2.pack_start(self.btn_cancel,False,False,0)
		self.vbox2.set_border_width(8)
		
		self.btn_cancel.connect("clicked",self.cancel)
		self.image.set_from_file(imagePath+"database.png")
		self.window.add(self.vbox2)
		self.window.connect("delete_event", self.delete)
		self.window.set_title("Capsules")
		self.window.resize(400,100)
		self.window.show_all()
		self.window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
		finishEvents()
		finishEvents()
	
	def commonfail(self,message):
		self.appendTextBuffer("failed: "+message)
		self.label.set_text(message)
		self.btn_cancel.hide()
		self.image.set_from_file(imagePath+"error.png")
		self.cont=False
	
	def printError(self, message):
		print "\n - Capsules system encountered a fatal error '"+str(message)+"'\n"
		self.cont = False
		return False
	
	def installCap(self,cap,status,output)):
		"""
			Prints information during an install of a capsule.
			status codes:
				status -1 = install failed
				status 0 = starting install
				status 0.5 = running pre install scripts
				status 0.9 = pre install scripts finished
				status -0.5 = pre install scripts finished
				status 1 = extracting
				status 2 = running post install scripts
				status 2.1 =c
				status 2.5 = post install scripts finished
				status -2 = post install scripts failed
				status 3 = finished installing
		"""
		self.progress.hide()
		if status == -1:
			self.window.set_title("Install failed on "+cap.capname)
			self.commonfail("Install failed on "+cap.capname)
		elif status == 0:
			self.window.set_title("Installing "+cap.capname)
			self.appendTextBuffer("installing "+cap.capname)
		elif status == 0.5:
			self.appendTextBuffer("Pre-Install scripts - "+cap.capname)
			self.image.set_from_file(imagePath+"script.png")
		elif status == -0.5:
			self.commonfail("Pre-Install scripts failed - "+cap.capname)
		elif status == 0.9:
			self.appendTextBuffer("Pre-Install scripts complete - "+cap.capname)
			self.image.set_from_file(imagePath+"script.png")
		elif status == 1:
			self.window.set_title("Extracting")
			self.appendTextBuffer("Extracting "+cap.capname)
			self.image.set_from_file(imagePath+"extract.png")
			self.progress.show()
		elif status == 2:
			self.window.set_title("Post-install scripts - "+cap.capname)
			self.appendTextBuffer("Post-install scripts - "+cap.capname)
			self.image.set_from_file(imagePath+"script.png")
		elif status == 2.5:
			self.window.set_title("Post-install scripts complete - "+cap.capname)
			self.appendTextBuffer("Post-install scripts complete - "+cap.capname)
			self.image.set_from_file(imagePath+"script.png")
		elif status == -2.5:
			self.commonfail("Post-install scripts failed - "+cap.capname)
		elif status == 3:
			self.window.set_title("Install Finished - "+cap.capname)
			self.appendTextBuffer("Install Finished - "+cap.capname)
			self.image.set_from_file(imagePath+"package-icon.png")
			self.progress.show()
			self.progress.set_fraction(1.0)
		
		finishEvents()
		return self.cont
	
	def getCap(self,cap,status):
		"""
			Prints information about what getcap is doing
			status codes:
				status 0 = starting getcap
				status 1 = resolving dependencies
				status 1.5 = dep resolution complete
				status 2 = downloading capsule
				status 3 = checking integrity
				status 4 = installing
				status 5 = finished
		"""
		self.progress.hide()
		if status == 0:
			self.window.set_title("Installing "+cap.capname)
		elif status == 1:
			self.window.set_title("Resolving deps...")
			self.appendTextBuffer("Resolving deps")
		elif status == 1.5:
			self.window.set_title("Resolved deps...")
		elif status == 2:
			self.window.set_title("Downloading "+cap.capname)
			self.image.set_from_file(imagePath+"download.png")
			self.progress.show()
		elif status == 3:
			self.window.set_title("Checking integrity...")
		elif status == 4:
			self.window.set_title("Installing")
		elif status == 5:
			self.window.set_title("Finished - "+cap.keyword)
			self.appendTextBuffer("Finished - "+cap.keyword)
			self.image.set_from_file(imagePath+"package-icon.png")
			self.progress.show()
			self.progress.set_fraction(1.0)
		finishEvents()
		return self.cont
		
	def downloadProgress(self,progress, status, filename, size):
		"""
			Called during a capsule download, to report progress.
		"""
		if status == 0:
			self.progress.set_fraction(0.0)
			self.appendTextBuffer("download:"+filename)
		elif status==1:
			if progress > 0:
				self.progress.set_fraction(progress/100.00)
		elif status==2:
			self.progress.set_fraction(1.0)
			self.appendTextBuffer("download finished")
		elif status==-1:
			self.progress.set_fraction(0.0)
			self.commonfail('download failed!')
		finishEvents()
		return self.cont
		
	def integrity(self,cap,status):
		"""
			Called before and after an integrity check.
			Reports when the integrity checks starts, and the status after it completes.
			status codes:
				status 0 = integrity check started
				status 1 = passed
				status -1 = failed
		"""
		if status == 0:
			self.label.set_text('Checking integrity...')
		elif status == 1:
			self.label.set_text('Integrity check passed!')
		elif status == -1:
			self.label.set_text('Integrity check failed!')
			self.cont=False
		finishEvents()
		return self.cont
		
	def extract(self, cap, status, curFileName,numFiles,currentFileNum,isCount):
		"""
			Called before, and during a capsule decompression/extraction.
			Reports back progress.
			status codes:
				status 0 = starting extraction of cap
				status 1 = extracting cap
				status 2 = capsule extraction finished
		"""
		if status == 0:
			self.progress.set_fraction(0.0)
		elif status == -1:
			self.label.set_text("Extract failed on '%s'" % curFileName)
			self.commonfail("Extract failed on '%s'" % curFileName)
			self.cont=False
		elif status == 2:
			self.progress.set_fraction(1.0)
			self.label.set_text('Finished')
		elif status ==1:
			if isCount:
				progress=(1.00*currentFileNum)/(1.00*numFiles)
				self.progress.set_fraction(progress)
			else:
				self.label.set_text(str(currentFileNum))
		finishEvents()
		return self.cont

class pygtk_ui(capsules.ui):
	
	window = gtk.Window(gtk.WINDOW_TOPLEVEL)
	showingabout=False
	
	search_Ent = gtk.Entry(max=0)
	result_lst = gtk.ListStore(str,str)
	result_record = gtk.TextBuffer()
	result_menu = gtk.Menu()
	
	tooltips = gtk.Tooltips()
	statusbar = gtk.Statusbar()
	
	def printForm(self,cap):
		return """
Keyword: %s
Capname: %s
Deps:
     %s
md5sum: %s
architecture: %s
version: %s
description:
     %s
urls:
     %s
""" % (cap.keyword,cap.capname,"\t".join(cap.deps,os.linesep),cap.md5,cap.arch,cap.version,cap.desc,"\t   ".join(cap.urls,os.linesep))
	
	def msgbox(self, message):
		message = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, message)
		message.connect("response", lambda self, widget: self.destroy())
		message.show()

	def errorbox(self, message):
		message = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, message)
		message.connect("response", lambda self, widget: self.destroy())
		message.show()

	def delete(self, widget, event=None):
		gtk.main_quit()
		return False
	
	def repo_changed(self,widget):
		self.result_record.set_text('')
		self.result_lst.clear()
		l_alias = self.cmb_repos.get_active_text()
		if l_alias != None:
			db.alias=l_alias
			db.depalias=l_alias
	
	def search_lst_click(self,treeview, path, view_column):
		val=''
		try:
			thisiter = self.result_lst.get_path(self.result_vw.get_selection().get_selected()[1])[0]
			val=self.result_lst.get_value(self.result_lst.get_iter(thisiter,), 1)
		except:
			self.errorbox("Choose a capsule from the list")
			return
		
		cap = capsules.capEntry()
		cap.parse(val)
		self.result_record.set_text(self.printForm(cap))
	
	def search_click(self,widget):
		term = self.search_Ent.get_text()
		self.result_lst.clear()
		
		if term == None:
			self.errorbox("Requires a search term")
			return
		elif term.strip() == '':
			self.errorbox("Requires a search term")
			return
		
		context_id = self.statusbar.get_context_id("searching")
		self.statusbar.push(context_id, "Searching repository...")
		caps=[]
		finishEvents()
		try:
			caps = db.getSearch(term,db.alias)
		except capsules.capsuleDatabaseError, (strerror):
			self.printError(strerror)
		
		for cap in caps:
			self.result_lst.append([cap.keyword,cap])
		
		self.statusbar.pop(context_id)
		self.search_Ent.set_text('')
	
	def deleteabout(self, widget, event=None):
		self.showingabout=False
	
	def showabout(self,widget):
		if self.showingabout:
			self.showingabout=False
			self.aboutwindow.destroy()
			return
		self.showingabout=True
		self.aboutwindow = gtk.AboutDialog()
		self.aboutwindow.connect("delete_event", self.deleteabout)
		self.aboutwindow.connect("response", self.deleteabout)
		self.aboutwindow.set_name("cap-pygtk")
		self.aboutwindow.set_version(version)
		self.aboutwindow.set_website("http://arcanelinux.org")
		self.aboutwindow.set_comments("Capsules package management pygtk frontend")
		self.aboutwindow.set_copyright(','.join(authors))
		logoImg = gtk.Image()
		logoImg.set_from_file(imagePath+"package-icon.png")
		self.aboutwindow.set_logo(logoImg.get_pixbuf())
		self.aboutwindow.show()
	
	def install_click(self,widget):
		val=''
		try:
			thisiter = self.result_lst.get_path(self.result_vw.get_selection().get_selected()[1])[0]
			val=self.result_lst.get_value(self.result_lst.get_iter(thisiter,), 1)
		except:
			self.errorbox("Choose a capsule from the list")
			return
		
		cap = capsules.capEntry()
		cap.parse(val)
		
		versions=capsules.getVersions(cap.keyword)
		if ( len(versions) > 0 ):
			outString = "\t\t"
			for version in versions:
				outString+=cap.keyword+'-'+version+"\n\t\t"
			self.errorbox("Package with the keyword '%s' already exists:\n%s\nuse upgrade feature or remove this package first" % (cap.keyword,outString))
			return
		
		context_id = self.statusbar.get_context_id("installing")
		self.statusbar.push(context_id, "Installing...")
		ui=pygtk_installer()
		capsules.getCap(cap,db,ui)
		ui.btn_cancel.hide()
		self.statusbar.pop(context_id)
	
	def install_local(self,widget):
		fname = self.flc_localcap.get_filename()
		if fname == None:
			self.errorbox("Choose a capsule to install")
			return
		cap = capsules.capEntry()
		cap.capname=fname.replace(".cap",'').strip()
		context_id = self.statusbar.get_context_id("installing local")
		self.statusbar.push(context_id, "Installing local...")
		ui=pygtk_installer()
		capsules.installCap(cap,ui)
		ui.btn_cancel.hide()
		self.statusbar.pop(context_id)
	
	def __init__(self):
		self.window.connect("delete_event", self.delete)
		
		# setup search --------------
		self.search_Ent.set_width_chars(50)
		self.search_Ent.connect("activate",self.search_click)
		
		self.result_vw = gtk.TreeView(self.result_lst)
		self.result_vw.connect("row-activated",self.search_lst_click)
		result_cell = gtk.CellRendererText()
		result_col = gtk.TreeViewColumn('Capsules', result_cell)
		
		self.result_vw.set_model(self.result_lst)
		self.result_vw.append_column(result_col)
		result_col.set_attributes(result_cell, text=0)
		
		scroller=gtk.ScrolledWindow()
		scroller.add_with_viewport(self.result_vw)
		scroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
		
		search_Btn = gtk.Button("Search",stock=gtk.STOCK_FIND)
		search_Btn.connect("clicked",self.search_click)
		self.tooltips.set_tip(search_Btn,"Find a capsule from the repository")
		
		lst_repos = gtk.ListStore(str)
		self.cmb_repos = gtk.ComboBox(lst_repos)
		cell_repos = gtk.CellRendererText()
		self.cmb_repos.pack_start(cell_repos, True)
		self.cmb_repos.add_attribute(cell_repos, 'text', 0)
		for rep in capsules.capWeb.defaults:
			lst_repos.append([rep])
		self.cmb_repos.set_active(0)
		self.cmb_repos.connect("changed",self.repo_changed)
		
		searchTable = gtk.Table(1,4,False)
		searchTable.attach(gtk.Label("Term:"),0,1,0,1,xoptions=gtk.SHRINK,yoptions=gtk.SHRINK)
		searchTable.attach(self.search_Ent,1,2,0,1,xoptions=gtk.SHRINK,yoptions=gtk.SHRINK)
		searchTable.attach(search_Btn,2,3,0,1,xoptions=gtk.SHRINK,yoptions=gtk.SHRINK)
		searchTable.attach(self.cmb_repos,3,4,0,1,xoptions=gtk.SHRINK,yoptions=gtk.SHRINK)
		
		res_scroller=gtk.ScrolledWindow()
		res_txtV = gtk.TextView(self.result_record)
		res_txtV.set_editable(False)
		res_scroller.add_with_viewport(res_txtV)
		res_scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		
		res_bar = gtk.Toolbar()
		res_install_btn = gtk.ToolButton(gtk.STOCK_NETWORK)
		res_install_btn.set_label("Install")
		res_install_btn.set_tooltip(self.tooltips,"Install capsule from repository")
		res_install_btn.connect("clicked",self.install_click)
		res_bar.insert(res_install_btn,0)
		res_about_btn = gtk.ToolButton(gtk.STOCK_ABOUT)
		res_about_btn.set_label("About")
		res_about_btn.set_tooltip(self.tooltips,"About cap-pygtk")
		res_about_btn.connect("clicked",self.showabout)
		res_bar.insert(res_about_btn,1)
		
		filt=gtk.FileFilter()
		filt.add_pattern("*.cap")
		self.flc_localcap=gtk.FileChooserButton("Select a capsule")
		self.flc_localcap.set_current_folder("/"+capsules.capdir)
		self.flc_localcap.connect("hide", self.install_local)
		self.flc_localcap.add_filter(filt)
		self.tooltips.set_tip(self.flc_localcap,"Install capsule from local directory")
		
		inst_Btn = gtk.Button("Local")
		im=gtk.Image()
		im.set_from_stock(gtk.STOCK_HARDDISK, gtk.ICON_SIZE_BUTTON)
		inst_Btn.set_image(im)
		inst_Btn.connect("clicked",self.install_local)
		self.tooltips.set_tip(inst_Btn,"Install a capsule from a local directory")
		
		linst_hbox = gtk.HBox(False,2)
		linst_hbox.pack_start(gtk.Label("Local Install:"),False,False,2)
		linst_hbox.pack_start(self.flc_localcap,True,True,2)
		linst_hbox.pack_start(inst_Btn,False,False,0)
		
		res_buttons_vbox=gtk.VBox(False,2)
		res_buttons_vbox.pack_start(res_bar,False,False,0)
		res_buttons_vbox.pack_start(res_scroller,True,True,0)
		res_buttons_vbox.pack_start(linst_hbox,False,True,0)
		
		search_hbox = gtk.HBox(False,2)
		search_hbox.pack_start(scroller,False,False,2)
		search_hbox.pack_start(res_buttons_vbox,True,True,0)
		
		search_vbox = gtk.VBox(False,2)
		search_vbox.pack_start(searchTable,False,False,0)
		search_vbox.pack_start(search_hbox,True,True,0)
		
		#-----------------------------------	
		
		main_icon=gtk.Image()
		main_icon.set_from_file(imagePath+"icon.png")
		gtk.window_set_default_icon_list(main_icon.get_pixbuf())
		
		self.tooltips.enable()
		
		topBox = gtk.VBox(False,0)
		search_vbox.set_border_width(4)
		topBox.pack_start(search_vbox,True,True,0)
		topBox.pack_start(self.statusbar,False,False,0)
		
		self.window.set_title("cap-pygtk")
		self.window.add(topBox)
		self.window.resize(300,450)
		self.window.show_all()
	
def main():
	gtk.main()
	return 0

if __name__=="__main__":
	g = pygtk_ui()
	main()